package com.ideal.gpt.ability.docqa.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.alibaba.fastjson.JSONObject;
import com.ideal.gpt.GptJobConfig;
import com.ideal.gpt.ability.docqa.QaFiller;
import com.ideal.gpt.ability.docqa.QaTF;
import com.ideal.gpt.ability.docqa.TF_Splitter;
import com.ideal.gpt.ability.docqa.TF_Splitter.TfInfo;
import com.ideal.gpt.ability.docqa.splitter.ResponseSplitter;
import com.ideal.gpt.ability.docqa.splitter.ResponseSplitter.SplitResult;
import com.ideal.gpt.app.docqajob.QaParseJob.QuestionType;
import com.lzwork.gpt.utility.caller.GptCallResult;
import com.lzwork.gpt.utility.common.ParserCaller;
import com.lzwork.gpt.utility.common.Passage;
import com.lzwork.utility.collection.CollUtility;
import com.lzwork.utility.logger.LOGGER;
import com.lzwork.utility.pair.StringPair;
import com.lzwork.utility.str.StrUtility;
import com.lzwork.web.WebResultCode;

/**
 * @author LiZheng
 * @date 2024年4月1日 下午5:22:42
 */

public class ParseQaCall
{
	public final static String DEF_PMT_DIFF = "对比以下两份报告之间的差异点：\n\n第一份报告：\n```{{first}}```\n第二份报告：\n```{{second}}```";
	public final static String DEF_PMT_REPORT = "你是一个智能制造管理员，现在需要根据一篇文章生成一份调查报告。报告为json格式，并需要包含以下内容：\n\n"
			+ "reportTitle：报告标题，\n" + "indexInterpret：指标解读，\n" + "maturityAssessment：智能制造能力成熟度评估结果分析，\n"
			+ "peoplePowerAssessment：智能制造人员能力要素评估结果，\n" + "technologyPowerAssessment：智能制造技术能力要素评估结果，\n"
			+ "resourcePowerAssessment：智能制造资源能力要素评估结果，\n" + "designPowerAssessment：智能制造设计能力要素评估结果，\n"
			+ "manufacturePowerAssessment：智能制造制造能力要素评估结果。\n\n"

			+ "文章的内容如下：\n```\n{{content}}\n```";

	public final static String pt_qa = "现有文本:```{0}```请针对上述文本提出{1}个问题，并给出对应的答案。"
			+ "每个问题用'问题'开头，每个回答用'回答'开头。要求用中文，必须与文本相关，同时不要使用'此文本'、'该措施'、'根据文本'等描述，请用完整的称呼{2}";
	public final static String pt_choice = "根据以下文本出{1}道选择题，每道题包括四个选项，正确的选项只有一个，每道题输出结果题目、选项、正确的选项：```{0}```";
	public final static String pt_choicem = "根据以下文本出{1}道选择题，每道题包括四个选项，正确的选项不止一个，每道题输出结果题目、选项、正确的选项：```{0}```";
	public final static String pt_tf = "根据以下文本出{1}道判断题，并给出答案。文本内容：```{0}```";

	public final static String xpt_qa = "现有文本:```{0}```请针对上述文本提出个问题。每个问题用'问题'开头，每个回答用'回答'开头。"
			+ "要求用中文，必须与文本相关，同时不要使用'此文本'、'该措施'、'根据文本'等描述，请用完整的称呼{2}";
	public final static String xpt_choice = "根据以下文本出{1}道选择题。要求：每道题包括四个选项，每道题输出结果题目、选项。不需要答案。文本如下：```{0}```";
	public final static String xpt_choicem = "根据以下文本出{1}道选择题，每道题包括四个选项，正确的选项不止一个，每道题输出结果题目、选项。不需要答案{2}。文本内容：```{0}```";
	public final static String xpt_tf = "根据以下文本出{1}道判断题。要求：题目需要针对文中内容做出判断，但不要给出答案，题目长度不超过50字。文本内容：```{0}```";

	final static String pt_fill = "根据以下文本提取{1}句核心句子，和句子中的关键词。文本内容：```{0}```";

	final static String DEF_KEY_CONTENT = "{0}";
	final static String DEF_KEY_NUM = "{1}";
	final static String DEF_KEY_ANSWER = "{2}";

	public ParseQaCall()
	{
		this.keyContent = DEF_KEY_CONTENT;
		this.keyNum = DEF_KEY_NUM;
		this.keyAnswer = DEF_KEY_ANSWER;
	}

	public ParseQaCall(String keyContent, String keyNum, String keyAnswer)
	{
		this.keyContent = keyContent;
		this.keyNum = keyNum;
		this.keyAnswer = keyAnswer;
	}

	String keyContent;
	String keyNum;
	String keyAnswer;

	public JSONObject parseQa(int callType, String content, QuestionType qType, int num, boolean noAnswer, boolean dolog)
	{
		int maxlen = GptJobConfig.getIntProp("qaMaxLen", -1);
		if (maxlen > 0)
		{
			int rawLen = content.length();
			content = Passage.cutPassage(content, maxlen, true);
			int nowLen = content.length();
			if (rawLen != nowLen)
				LOGGER.log(LOGGER.NET,
						"Cut input prompt from " + rawLen + " to " + nowLen + " with max length limit: " + maxlen);
		}

		switch (callType)
		{
		case 1:
			return commonQa(content, qType, num, noAnswer, dolog);
		case 2:
			return callerQa(content, qType, num, noAnswer, dolog);
		case 3:
			return apiQa(content, false, qType, num, noAnswer, dolog);
		case 4:
			return apiQa(content, true, qType, num, noAnswer, dolog);
		default:
			return WebResultCode.PARAMETER_INVALID.toErrJobj("Invalid calltype: " + callType);
		}
	}

	Random rand = new Random();

	String getApiKey(QuestionType qType, boolean rawParse)
	{
		if (rawParse)
		{
			switch (qType)
			{
			case Choice1:
				return "raw_choice";
			case ChoiceM:
				return "raw_choicem";
			case QA:
				return "raw_qa";
			case TF:
				return "raw_tf";
			case Fill:
				return "raw_fill";
			default:
				break;
			}

		} else
		{
			String urlKey = "";
			switch (qType)
			{
			case Choice1:
				urlKey = "pt_choice";
				break;
			case ChoiceM:
				urlKey = "pt_choicem";
				break;
			case QA:
				urlKey = "pt_qa";
				break;
			case TF:
				urlKey = "pt_keyw";
				break;
			case Fill:
				urlKey = "pt_keyw";
				break;
			default:
				break;
			}
			return GptJobConfig.getProp(urlKey);
		}

		return "";
	}

	protected JSONObject apiQa(String content, boolean rawParse, QuestionType qType, int num, boolean noAnswer, boolean dolog)
	{
		LOGGER.log(LOGGER.APP, "Do Qa parse [" + qType.name() + "] by Api call.");

		JSONObject param = new JSONObject(true);
		param.put(keyContent, content);
		param.put(keyNum, num);
		param.put(keyAnswer, noAnswer ? "不包括答案" : "包括答案");
		param.put("dolog", dolog);

		String fullUrl = GptJobConfig.getProp("api_server");
		String apiKey = getApiKey(qType, rawParse);
		fullUrl += "/callapi?apikey=" + apiKey;

		ParserCaller caller = new ParserCaller(fullUrl, dolog);
		GptCallResult cr = caller.call(fullUrl, param);

		JSONObject errObj = cr.getError();
		if (errObj != null)
			return errObj;

		String response = cr.getResponse();
		JSONObject result = new JSONObject();

		float cost = cr.getTimeCost();

		if (rawParse)
		{
			result.put("content", response);
		} else if (qType == QuestionType.Fill)
		{
			int max = Math.min(3, num);

			ResponseSplitter splitter = new ResponseSplitter(max, dolog);
			List<StringPair> qasp = splitter.splitResponse(response, 14).target();

			if (!CollUtility.hasData(qasp))
			{
				TF_Splitter tfSplitter = new TF_Splitter(max, dolog);
				List<TfInfo> sents = tfSplitter.keywordSplit(response);
				qasp = QaFiller.makeQuestions(sents);
			}

			if (CollUtility.hasData(qasp))
				result.put("content", QaFiller.resultToString(qasp, noAnswer));
			else
			{
				if (dolog)
					LOGGER.log(LOGGER.PARSE, "Get keywords from response fail: " + response);
				result.put("content", response);
			}

		} else if (qType == QuestionType.TF)
		{
			TF_Splitter splitter = new TF_Splitter(num, dolog);

			List<TfInfo> sents = splitter.keywordSplit(response);

			List<String> contentItems = new ArrayList<>();
			int index = 0;

			for (TfInfo tfItem : sents)
			{
				index++;
				String sent = ResponseSplitter.removeQuote(tfItem.getSent());

				String desc = "QA filler parse " + index + "/" + sents.size() + " ["
						+ (sents.isEmpty() ? "Random Sent" : "Core Sent") + "]";

				if (dolog)
					LOGGER.log(LOGGER.APP, desc + " with sentence: " + sent);

				String subUrl = fullUrl + GptJobConfig.getProp("pt_tfcheck");

				boolean tf = rand.nextBoolean();

				param = new JSONObject();

				param.put(keyContent, sent);
				param.put(keyNum, tf ? "但不改变原句的意义" : "让它表达出和原句相反的含义");
				param.put("dolog", dolog);
				param.put("desc", desc);

				caller = new ParserCaller(subUrl, dolog);
				cr = caller.call(subUrl, param);

				cost += cr.getTimeCost();

				errObj = cr.getError();
				if (errObj != null)
					return errObj;

				String tfResponse = cr.getResponse();
				QaTF item = new QaTF(tfResponse, tf);

				String outString = item.toQuestion(tfItem.getTitle(), noAnswer);

				if (item.getQuestion().isEmpty())
				{
					LOGGER.log(LOGGER.APP, "Invalid changer fetch from:\n---Response---" + tfResponse + "\n---Post---"
							+ item.getResponse() + "\nby input: [" + sent + "]");
				}

				contentItems.add(outString);

			}
			result.put("content", StrUtility.combineString(contentItems, "\n\n"));

		} else
			result.put("content", response);

		result.put("cost", cost);

		return WebResultCode.convert(result, WebResultCode.GPU_CALL_ERROR);
	}

	protected JSONObject callerQa(String content, QuestionType qType, int num, boolean noAnswer, boolean dolog)
	{
		LOGGER.log(LOGGER.APP, "Do Qa parse [" + qType.name() + "] by local prompt call.");

		String prompt = "";

		switch (qType)
		{
		case Choice1:
			prompt = noAnswer ? xpt_choice : pt_choice;
			break;
		case ChoiceM:
			prompt = noAnswer ? xpt_choicem : pt_choicem;
			break;
		case QA:
			prompt = noAnswer ? xpt_qa : pt_qa;
			break;
		case TF:
			prompt = noAnswer ? xpt_tf : pt_tf;
			break;
		case Fill:
			prompt = pt_fill;
			break;
		default:
			break;
		}

		ParserCaller caller = new ParserCaller(GptJobConfig.url(), dolog);
		prompt = prompt.replace(keyContent, content);
		prompt = prompt.replace(keyNum, Integer.toString(num));

		GptCallResult cr = caller.callChat(prompt, "make report", dolog);
		JSONObject errObj = cr.getError();
		if (errObj != null)
			return errObj;

		String response = cr.getResponse();
		JSONObject result = new JSONObject();

		if (qType == QuestionType.Fill)
		{
			ResponseSplitter splitter = new ResponseSplitter(num, dolog);
			SplitResult qasp = splitter.splitResponse(cr.getResponse(), 14);

			if (qasp.isEmpty())
				result.put("content", cr.getResponse());
			else
				result.put("content", QaFiller.resultToString(qasp.target(), noAnswer));
		} else
			result.put("content", response);

		result.put("cost", cr.getGpuCost());

		return WebResultCode.convert(result, WebResultCode.GPU_CALL_ERROR);
	}

	protected JSONObject commonQa(String content, QuestionType qType, int num, boolean noAnswer, boolean dolog)
	{
		LOGGER.log(LOGGER.APP, "Do Qa parse [" + qType.name() + "] by server call.");

		JSONObject result = DocQaController.parseQa(GptJobConfig.url(), qType.bit(), content, noAnswer, num, dolog);
		return WebResultCode.convert(result, WebResultCode.GPU_CALL_ERROR);
	}
}
